package com.katesoft.scale4j.web.services;

import java.util.Collection;

import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.lang.RandomStringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Required;
import org.springframework.dao.DataAccessException;
import org.springframework.security.access.annotation.Secured;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.transaction.annotation.Transactional;

import com.katesoft.scale4j.log.LogFactory;
import com.katesoft.scale4j.log.Logger;
import com.katesoft.scale4j.web.model.Role;
import com.katesoft.scale4j.web.model.User;
import com.katesoft.scale4j.web.model.UserLocale;
import com.katesoft.scale4j.web.security.ISecurityRoles;
import com.katesoft.scale4j.web.support.WebappDaoSupport;

/**
 * Contains common functionality around user/roles/locale stuff.
 * 
 * @author kate2007
 */
@Transactional(rollbackFor = Exception.class)
public class UserRoleService extends WebappDaoSupport implements IUserRoleService {
   private final Logger logger = LogFactory.getLogger(UserRoleService.class);
   private UserDetailsService userDetailsService;
   private IUserNotificationService userNotificationService;

   @Transactional(readOnly = true)
   @Override
   public Collection<Role> findAllRoles() {
      return getLocalHibernateTemplate().loadAll(Role.class, true);
   }

   @Transactional(readOnly = true)
   @Override
   public Collection<UserLocale> findAllLocales() {
      return getLocalHibernateTemplate().loadAll(UserLocale.class, true);
   }

   @Secured(ISecurityRoles.IS_AUTHENTICATED_ANONYMOUSLY)
   @Override
   @Transactional(readOnly = true)
   public String requestNewPassword(final String userName) {
      User user;
      try {
         user = (User) userDetailsService.loadUserByUsername(userName);
      } catch (UsernameNotFoundException e) {
         logger.error("User with username[%s] does not exist", userName);
         throw e;
      }
      String newPassword = RandomStringUtils.randomAlphanumeric(15);
      logger.debug("generated new password[%s] for username[%s]", newPassword, userName);
      user.setPassword(DigestUtils.shaHex(newPassword));
      if (userNotificationService != null) {
         userNotificationService.notifyNewPassword(newPassword, user);
      }
      return newPassword;
   }

   @Override
   public void updatePassword(String newPassword, String username) {
      final User user = loadUser(username);
      user.setPassword(newPassword);
      getLocalHibernateTemplate().update(user);
      getLocalHibernateTemplate().flush();
      if (userNotificationService != null) {
         userNotificationService.notifyPasswordUpdated(newPassword, user);
      }
   }

   @Transactional(readOnly = true)
   @Override
   public UserLocale getUserPreferableLocale(String username) {
      User user = loadUser(username);
      return user.getUserPreferences().getLocale();
   }

   @Override
   public void updateUserPreferableLocale(String newLocale, String username) {
      UserLocale locale = loadLocale(newLocale);
      User user = loadUser(username);
      user.getUserPreferences().setLocale(locale);
      getLocalHibernateTemplate().update(user);
      getLocalHibernateTemplate().flush();
      if (userNotificationService != null) {
         userNotificationService.notifyUserPreferableLocaleUpdated(newLocale, user);
      }
   }

   @Transactional(readOnly = true)
   @Override
   public User loadUserByUsername(final String username) throws UsernameNotFoundException,
            DataAccessException {
      return (User) userDetailsService.loadUserByUsername(username);
   }

   @Transactional(readOnly = true)
   @Override
   public Collection<User> search(String queryString) {
      return search(queryString, User.class, new String[] { User.PROP_LOGIN, User.PROP_EMAIL });
   }

   @Override
   @Transactional(readOnly = true)
   public UserLocale loadLocale(String code) {
      return super.loadLocale(code);
   }

   public void deleteUser(final User user) {
      getLocalHibernateTemplate().delete(user);
      getLocalHibernateTemplate().flush();
   }

   public void saveOrUpdateUser(User user) {
      user.getAccount();
      user.getPerson();
      if (user.getRoles() == null || user.getRoles().isEmpty()) {
         user.getRoles().add(loadRoleByName(ISecurityRoles.ROLE_USER));
      }
      getLocalHibernateTemplate().saveOrUpdate(user);
      getLocalHibernateTemplate().flush();
   }

   // ***********************************************************************
   // ************************* (getters+setters) ***************************
   // ***********************************************************************

   @Required
   @Autowired
   public void setUserDetailsService(UserDetailsService userDetailsService) {
      this.userDetailsService = userDetailsService;
   }

   public void setUserNotificationService(IUserNotificationService userNotificationService) {
      this.userNotificationService = userNotificationService;
   }
}
